const { app, BrowserWindow, BrowserView, protocol, ipcMain} = require('electron')
require('chromedriver');
// var webdriver = require('selenium-webdriver');
const { Builder, By, until } = require('selenium-webdriver');

const Global = require('./Global.js');

// 接收执行自动化指令
module.exports.initHandler = function(win){
    console.log("X6Core Init");
    ipcMain.on('X6Core', (action) => {
       console.log("接收到指令", action);
    });

    ipcMain.on('execute_env', async (event, arg)=>{
        console.log("接收到指令 execute_env", arg);
        if(arg.action == 'openChrome'){
            let env = JSON.parse(arg.data);
            initEnv(env['envID'], env);
            await openNewTab(env['envID'], env.url);
            win.webContents.send('updateOpenedEnvIDs', Object.keys(Global.envs));
        }else if(arg.action == 'closeChrome') {
            let env = JSON.parse(arg.data);
            const driver = Global.envs[env['envID']];
            await driver.quit();
            delete Global.envs[env['envID']];
            win.webContents.send('updateOpenedEnvIDs', Object.keys(Global.envs));
        }else if(arg.action == 'getOpenedEnvs'){
            event.returnValue = Object.keys(Global.envs);
        }
    })

    ipcMain.on('execute_rpa', async (event, arg)=>{
        console.log("接收到指令 execute_rpa", arg.action, JSON.parse(arg.data));
        if(arg.action == "executeTask"){
            let task = JSON.parse(arg.data);
            const x6Data = JSON.parse(task.taskData);
            console.log("x6Data", x6Data);
            let executeStart = new Date().getTime();
            await RPARunner(x6Data);
            let executeEnd = new Date().getTime();
            task.executeStart = executeStart;
            task.executeEnd = executeEnd;
            task.taskStatus = "已完成";
            win.webContents.send('executeTaskEnd', task);
            console.log('executeTaskEnd', task);
        }
    })

    ipcMain.on('getOpened', (event, arg) => {
        event.returnValue = eval(arg);
    })
}


const RPARunner = async (jsonData)=>{
    const nodes = converter(jsonData);
    console.log("[RPARunner]", nodes);
    let envID = "default";
    for (let index = 0; index < nodes.length; index++) {
        const node = nodes[index];
        console.log("[RPARunner] run node ", JSON.stringify(node));
        if(node.data.id == "StartNode"){
            // 初始化环境
            envID = node.data.properties[1].items[0].val;
            initEnv(envID);
        }else if(node.data.id == "NewTabNode"){
            let url = node.data.properties[1].items[0].val;
            await openNewTab(envID, url);
        }else if(node.data.id == "InputNode"){
            const css = node.data.properties[1].items[0].val;
            const content = node.data.properties[1].items[1].val;
            const delay = node.data.properties[1].items[2].val;
            await sendKeys(envID, css, content, delay);
            await waitSeconds(envID, parseInt(delay));
        }else if(node.data.id == "ClickNode"){
            const css = node.data.properties[1].items[0].val;
            await clickElement(envID, css);
        }else if(node.data.id == "ENdNode"){
            
        }
    }
}

//初始化环境
function initEnv(envID){
    var driver = new Builder()
    .forBrowser('chrome')
    .build();
    Global.envs[envID] = driver;
    console.log('Global', Global.envs);
}

// 等待x秒
async function waitSeconds (envID, timeout){
    console.log('waitSeconds', envID, timeout);
    await new Promise(res => setTimeout(res, timeout));
}

// 等待元素
async function waitElements (envID, css, timeout, looptime){
    console.log('waitElements', envID, seconds);
    const driver = Global.envs[envID]; 
    try {
        // 设置30秒超时时间和1秒的轮询间隔时间
        await driver.wait(until.elementLocated(By.css(css)), timeout, 'Timed out after 30 seconds', looptime);
    }catch(err){
        console.log("err", err);
    };
}

// 新标签
async function openNewTab (envID, url) {
    console.log('openNewTab', envID, url);
    const driver = Global.envs[envID]; 
    try {
        await driver.switchTo().newWindow('tab');
        await driver.get(url);
    }catch(err){
        console.log("err", err);
    };
}

// 关闭标签
async function closeTab (envID) {
    console.log('closeTab', envID);
    const driver = Global.envs[envID]; 
    try {
        await driver.close();
    }catch(err){
        console.log("err", err);
    };
}

// 打开URL
async function openUrl (envID, url) {
    console.log('openUrl', envID, url);
    const driver = Global.envs[envID]; 
    try {
        await driver.get(url);
    }catch(err){
        console.log("err", err);
    };
}

// 点击元素
async function clickElement (envID, css){
    console.log('clickElement', envID, css);
    const driver = Global.envs[envID]; 
    try {
        await driver.findElement(By.css(css)).click();
    }catch(err){
        console.log("err", err);
    };
}

// 输入内容
async function sendKeys (envID, css, content) {
    console.log('sendKeys', envID, css, content);
    const driver = Global.envs[envID]; 
    try {
        await driver.findElement(By.css(css)).sendKeys(content);
    }catch(err){
        console.log("err", err);
    };
}


const converter = (jsonData)=>{
    // 从StartNode开始, 一个一个节点查找，直到找到EndNode
    let node_cells = cloneDeep(jsonData.cells.filter((item)=>item.shape != 'edge'));
    let edge_cells = cloneDeep(jsonData.cells.filter((item)=>item.shape == 'edge'));

    if(!node_cells || node_cells.length<=0){
        return;
    }
    
    // 1. X6的数据结构，所有的cells是平铺的, 需要改成非平铺。
    // 所以可以通过遍历, 将children中的节点ID，替换成真实的节点
    console.log('replaceChildrenIdWithObject start');
    let replaced_node_celles = replaceChildrenIdWithObject(node_cells);
    console.log('replaceChildrenIdWithObject end', replaced_node_celles);

    // 2. 从Start节点开始查找连接的每个节点，生成一个新的有序数组。
    console.log('Resort Nodes From Start');
    let startNode = findStartNode(node_cells);
    let sortedNodes = [startNode];
    let currentNode = startNode;
    let loop = 0;// 最多找500次。
    while(true){
        if(loop>500) { alert('节点过多'); break;}
        console.log('loop', loop);
        const next = findNextNode(currentNode, node_cells, edge_cells);
        if(next) {
            currentNode = next;
            sortedNodes.push(currentNode);
        }else{
            break;
        }
        loop++;
    }
    console.log('Resort Nodes From Start', sortedNodes);


    // 3. 检查节点和连线是否正确
    // 规则1: 检查新数组的大小是否和原数组大小一致，若不一致，则判定有孤岛节点。
    // 检查2: 第一个节点必须是开始节点， 最后一个节点是结束节点。
    // 检查3: 并行分组内，必须有且只有一个节点是没有output连线的，有且只有一个节点是没有input连线的，其他节点都是有连线的。没有input的节点是开始节点，没有output连线的是结束节点。

    // 规则1
    if(replaced_node_celles.length != sortedNodes.length){
        alert('请确保所有连线正确');
        return;
    }

    // 规则2
    let lastNode = sortedNodes[sortedNodes.length - 1];
    if(lastNode.data.id != 'EndNode'){
        alert('最后一个节点非[结束]节点!');
        return;
    }

    return sortedNodes;
}

const cloneDeep = (obj)=>{
    let str = JSON.stringify(obj);
    return JSON.parse(str);
}

// 将children中的节点ID，替换成真实的节点
const replaceChildrenIdWithObject = (node_cells)=>{
    let childCellIds = [];
    for (let index = 0; index < node_cells.length; index++) {
        const node_cell = node_cells[index];
        if(node_cell.children) {
            let newChildren = [];
            node_cell.children.forEach((childCellId) => {
                childCellIds.push(childCellId);
                const childCell = findNodeById(childCellId, node_cells);
                newChildren.push(childCell);
            });
            node_cell.children = newChildren;
        }
    }

    // 从 node_cells 中过滤掉拥有父节点的节点
    let new_node_cells = node_cells.filter((item) => childCellIds.indexOf(item.id)<0)
    return new_node_cells;
}

const findStartNode = (node_cells) => {
    let filterStartNodes = node_cells.filter((item)=>item.data && item.data.id == 'StartNode');
    if(filterStartNodes && filterStartNodes.length >0){
        return filterStartNodes[0];
    }else{
        return null;
    }
}

const findNextNode = (node_cell, node_cells, edge_cells)=>{
    let cellId = node_cell.id;
    if(!(node_cell.ports)){ // End节点，没有ports
        return null;
    }
    let filterOutput = node_cell.ports.items.filter((item)=>item.group == 'output');
    if(filterOutput && filterOutput.length>0){
        let outputId = filterOutput[0].id;
        let edgeFilter = edge_cells.filter((item)=>item.source.cell == cellId && item.source.port == outputId);
        if(edgeFilter && edgeFilter.length>0){
            let edge = edgeFilter[0];
            let nextCellId = edge.target.cell;
            let nextCell = findNodeById(nextCellId, node_cells);
            return nextCell;
        }else{
            return null;
        }
    }else{
        return null;
    }
}
const findNodeById = (id, node_cells)=>{
    let cellFilter = node_cells.filter((item)=>item.id == id);
    if(cellFilter && cellFilter.length>0){
        return cellFilter[0];
    }else{
        return  null;
    }
}